/*
 * $Id: twitter.c,v 1.14 2010/04/05 16:42:24 fabio Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <gtk/gtk.h>
#include <curl/curl.h>
#include <libxml/parser.h>
#include "conf.h"
#include "twitter.h"

#define TWITTER_UPDATE_URL "http://twitter.com/statuses/update.xml"
#define TWITTER_STATUS_URL "http://twitter.com/statuses/friends_timeline.xml"

typedef struct _PROCESS_THREAD_INFO {
    GThreadFunc func;
    gboolean processing;
    gpointer data;
    gpointer retval;
} PROCESS_THREAD_INFO;

/**
 * string utilities
 */
static char* xml_decode_alloc(char* str) {
    char* buf = NULL;
    unsigned char* pbuf = NULL;
    int len = 0;

    if (!str) return NULL;
    len = strlen(str)*3;
    buf = malloc(len+1);
    memset(buf, 0, len+1);
    pbuf = (unsigned char*)buf;
    while(*str) {
        if (!memcmp(str, "&amp;", 5)) {
            strcat((char*)pbuf++, "&");
            str += 5;
        } else
            if (!memcmp(str, "&nbsp;", 6)) {
                strcat((char*)pbuf++, " ");
                str += 6;
            } else
                if (!memcmp(str, "&quot;", 6)) {
                    strcat((char*)pbuf++, "\"");
                    str += 6;
                } else
                    if (!memcmp(str, "&nbsp;", 6)) {
                        strcat((char*)pbuf++, " ");
                        str += 6;
                    } else
                        if (!memcmp(str, "&lt;", 4)) {
                            strcat((char*)pbuf++, "<");
                            str += 4;
                        } else
                            if (!memcmp(str, "&gt;", 4)) {
                                strcat((char*)pbuf++, ">");
                                str += 4;
                            } else
                                *pbuf++ = *str++;
    }
    return buf;
}

static char* url_encode_alloc(char* str) {
    static const int force_encode_all = TRUE;
    const char* hex = "0123456789abcdef";

    char* buf = NULL;
    unsigned char* pbuf = NULL;
    int len = 0;

    if (!str) return NULL;
    len = strlen(str)*3;
    buf = malloc(len+1);
    memset(buf, 0, len+1);
    pbuf = (unsigned char*)buf;
    while(*str) {
        unsigned char c = (unsigned char)*str;
        if (c == ' ')
            *pbuf++ = '+';
        else if (c & 0x80 || force_encode_all) {
            *pbuf++ = '%';
            *pbuf++ = hex[c >> 4];
            *pbuf++ = hex[c & 0x0f];
        } else
            *pbuf++ = c;
        str++;
    }
    return buf;
}

/**
 * processing message funcs
 */
static gpointer process_thread(gpointer data) {
    PROCESS_THREAD_INFO* info = (PROCESS_THREAD_INFO*)data;

    info->retval = info->func(info->data);
    info->processing = FALSE;

    return info->retval;
}

static gpointer process_func(GThreadFunc func, gpointer data) {
    PROCESS_THREAD_INFO info;
    GError *error = NULL;
    GThread* thread;

    gdk_threads_leave();

    info.func = func;
    info.data = data;
    info.retval = NULL;
    info.processing = TRUE;
    thread = g_thread_create(
            process_thread,
            &info,
            TRUE,
            &error);
    while(info.processing) {
        gdk_threads_enter();
        while(gtk_events_pending())
            gtk_main_iteration_do(TRUE);
        gdk_threads_leave();
        g_thread_yield();
    }
    g_thread_join(thread);

    gdk_threads_enter();

    return info.retval;
}

/**
 * curl callback
 */
static char* response_mime = NULL;	/* response content-type. ex: "text/html" */
static char* response_data = NULL;	/* response data from server. */
static size_t response_size = 0;	/* response size of data */

    static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream) {
        if (!response_data)
            response_data = (char*)malloc(size*nmemb);
        else
            response_data = (char*)realloc(response_data, response_size+size*nmemb);
        if (response_data) {
            memcpy(response_data+response_size, ptr, size*nmemb);
            response_size += size*nmemb;
        }
        return size*nmemb;
    }

static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream) {
    char* header = NULL;

    header = malloc(size*nmemb + 1);
    memcpy(header, ptr, size*nmemb);
    header[size*nmemb] = 0;
    if (strncmp(header, "Content-Type: ", 14) == 0) {
        char* stop = header + 14;
        stop = strpbrk(header + 14, "\r\n;");
        if (stop) *stop = 0;
        response_mime = strdup(header + 14);
    }
    free(header);
    return size*nmemb;
}

/**
 * post my status
 */
static gpointer post_status_thread(gpointer data) {
    CURL* curl = NULL;
    CURLcode res = CURLE_OK;
    long status = 0;
    ProgressData *pdata=(ProgressData *)data;

    char url[2048];
    char auth[512];
    char* message=strdup(pdata->last_message);
    g_free(pdata->last_message);
    gpointer result_str=NULL;

    if (!message || strlen(message) == 0) return NULL;

    /* making authenticate info */
    memset(url, 0, sizeof(url));
    strncpy(url, TWITTER_UPDATE_URL, sizeof(url)-1);
    message = url_encode_alloc(message);
    if (message) {
        strncat(url, "?status=", sizeof(url)-1);;
        strncat(url, message, sizeof(url)-1);
        free(message);
    }
    memset(auth, 0, sizeof(auth));

    snprintf(auth, sizeof(auth)-1, "%s:%s", pdata->twitter_username, pdata->twitter_password);

    /* initialize callback data */
    response_mime = NULL;
    response_data = NULL;
    response_size = 0;

    /* perform http */
    curl = curl_easy_init();
    if (!curl) return NULL;
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "");
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
    curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
    res = curl_easy_perform(curl);
    res == CURLE_OK ? curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &status) : res;
    curl_easy_cleanup(curl);

    if (status != 200) {
        /* failed to the post */
        if (response_data) {
            char* message;
            char* recv_data = malloc(response_size+1);
            memset(recv_data, 0, response_size+1);
            memcpy(recv_data, response_data, response_size);
            message = xml_decode_alloc(recv_data);
            result_str = g_strdup(message);
            free(message);
        } else
            result_str = g_strdup("unknown server response");
    } else {
        /* succeeded to the post */
        result_str = g_strdup("succeeded to the post");
    }

    /* cleanup callback data */
    if (response_mime) free(response_mime);
    if (response_data) free(response_data);
    response_data = NULL;
    response_mime = NULL;
    response_size = 0;
    log_message("twitter status: %d",status);

    return result_str;
}

void post_status(gpointer data) {
    gpointer result;
    ProgressData *pdata=(ProgressData *)data;

//    result = process_func(post_status_thread, pdata);
    result = post_status_thread(pdata);
    if (result) {
        log_message("twitter post result: %s",result);
        g_free(result);
    }
}
